﻿using BDRUtils.Extensions;
using System;
using System.Collections.Generic;
using System.DrawingCore;
using System.DrawingCore.Imaging;
using System.IO;
using System.Linq;
using System.Text;
using ZipFile = System.IO.Compression.ZipFile;

namespace BDRUtils.Utility
{
    /// <summary>
    /// 和文件相关的操作，如获取文件MD5值
    /// </summary>
    public static class FileInfoUtil
    {
        /// <summary>
        /// 获取文件MD5值
        /// </summary>
        /// <param name="fileName">文件绝对路径</param>
        /// <returns>MD5值</returns>
        public static string GetMD5Hash(string fileName)
        {
            try
            {
                using (FileStream stream = new FileStream(fileName, FileMode.Open))
                {
                    return GetMD5Hash(stream);
                }

            }
            catch (Exception ex)
            {
                throw new Exception("GetMD5HashFromFile() fail,error:" + ex.Message);
            }
        }

        public static string GetMD5Hash(Stream stream)
        {
            var sb = new StringBuilder();
            System.Security.Cryptography.MD5 md5 = new System.Security.Cryptography.MD5CryptoServiceProvider();
            byte[] retVal = md5.ComputeHash(stream);

            for (int i = 0; i < retVal.Length; i++)
            {
                sb.Append(retVal[i].ToString("x2"));
            }

            return sb.ToString();
        }

        /// <summary>
        /// 获取相对路径
        /// </summary>
        /// <param name="absolutePath"></param>
        /// <param name="relativeTo"></param>
        /// <returns></returns>
        public static string GetRelativePath(string absolutePath, string relativeTo)
        {
            string[] absoluteDirectories = absolutePath.Replace("/", "\\").Split('\\'); //replace兼容linux
            string[] relativeDirectories = relativeTo.Replace("/", "\\").Split('\\');


            //Get the shortest of the two paths
            int length = absoluteDirectories.Length < relativeDirectories.Length
                ? absoluteDirectories.Length
                : relativeDirectories.Length;

            //Use to determine where in the loop we exited
            int lastCommonRoot = -1;
            int index;

            //Find common root
            for (index = 0; index < length; index++)
                if (absoluteDirectories[index] == relativeDirectories[index])
                    lastCommonRoot = index;
                else
                    break;

            //If we didn't find a common prefix then throw
            if (lastCommonRoot == -1)
                throw new ArgumentException("Paths do not have a common base");

            //Build up the relative path
            StringBuilder relativePath = new StringBuilder();

            //Add on the ..
            for (index = lastCommonRoot + 1; index < absoluteDirectories.Length; index++)
                if (absoluteDirectories[index].Length > 0)
                    relativePath.Append("..\\");

            //Add on the folders
            for (index = lastCommonRoot + 1; index < relativeDirectories.Length - 1; index++)
                relativePath.Append(relativeDirectories[index] + "\\");
            relativePath.Append(relativeDirectories[relativeDirectories.Length - 1]);

            string result = relativePath.ToString().Replace("\\", "/");
            result = "/" + result;
            return result;
        }

        public static string GetBase64Data(FileStream filestream)
        {
            byte[] bt = new byte[filestream.Length];
            //调用read读取方法
            filestream.Read(bt, 0, bt.Length);
            string base64Str = Convert.ToBase64String(bt);
            filestream.Close();
            return base64Str;
        }

        /// <summary>
        /// StreamToBytes
        /// </summary>
        /// <param name="stream"></param>
        /// <returns></returns>
        public static byte[] StreamToBytes(Stream stream)
        {
            byte[] bytes = new byte[stream.Length];
            stream.Read(bytes, 0, bytes.Length);
            // 设置当前流的位置为流的开始
            stream.Seek(0, SeekOrigin.Begin);
            return bytes;
        }

        /// <summary>
        /// 文件转byte[]
        /// </summary>
        /// <param name="fileName"></param>
        /// <returns></returns>
        public static byte[] FileToByteArray(string fileName)
        {
            using (var stream = new FileInfo(fileName).OpenRead())
            {
                var buffer = new Byte[stream.Length];
                //从流中读取字节块并将该数据写入给定缓冲区buffer中
                stream.Read(buffer, 0, Convert.ToInt32(stream.Length));
                return buffer;
            }
        }

        /// <summary>
        /// 合并路径
        /// </summary>
        /// <param name="paths"></param>
        /// <returns></returns>
        public static string Combine(params string[] paths)
        {
            if (paths.Length == 0)
            {
                return default;
            }
            else
            {
                StringBuilder builder = new StringBuilder();
                string spliter = "\\";

                string firstPath = paths[0];

                if (firstPath.StartsWith("HTTP", StringComparison.OrdinalIgnoreCase))
                {
                    spliter = "/";
                }

                if (!firstPath.EndsWith(spliter))
                {
                    firstPath = firstPath + spliter;
                }

                builder.Append(firstPath);

                for (int i = 1; i < paths.Length; i++)
                {
                    string nextPath = paths[i];
                    if (string.IsNullOrWhiteSpace(nextPath))
                        continue;

                    if (nextPath.StartsWith("/") || nextPath.StartsWith("\\"))
                    {
                        nextPath = nextPath.Substring(1);
                    }

                    if (i != paths.Length - 1) //not the last one
                    {
                        if (nextPath.EndsWith("/") || nextPath.EndsWith("\\"))
                        {
                            nextPath = nextPath.Substring(0, nextPath.Length - 1) + spliter;
                        }
                        else
                        {
                            nextPath = nextPath + spliter;
                        }
                    }

                    builder.Append(nextPath);
                }
                return builder.ToString().Replace("\\", "/").TrimEnd('/');
            }
        }

        /// <summary>
        /// bmp转base64
        /// </summary>
        /// <param name="bmp"></param>
        /// <returns></returns>
        public static string ImgToBase64String(Bitmap bmp)
        {
            try
            {
                MemoryStream ms = new MemoryStream();
                bmp.Save(ms, ImageFormat.Jpeg);
                byte[] arr = new byte[ms.Length];
                ms.Position = 0;
                ms.Read(arr, 0, (int)ms.Length);
                ms.Close();
                return Convert.ToBase64String(arr);
            }
            catch
            {
                return default;
            }
        }

        /// <summary>
        /// 文件目录如果不存在，就创建一个新的目录
        /// </summary>
        /// <param name="path"></param>
        public static void DicCreate(string path)
        {
            try
            {
                path = path.ToCompatibilityPath();//转兼容路径
                if (!Directory.Exists(path))
                {
                    Directory.CreateDirectory(path);
                }
            }
            catch
            {
                //Console.WriteLine(e);
                throw;
            }

        }

        /// <summary>
        /// 解压Zip文件包
        /// </summary>
        /// <param name="zipPath"></param>
        /// <param name="extractPath"></param>
        public static string ExtractToDirectory(string zipPath, string extractPath)
        {
            try
            {
                Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);

                ZipFile.ExtractToDirectory(zipPath.ToCompatibilityPath(), extractPath.ToCompatibilityPath(), Encoding.GetEncoding("GB2312"));
            }
            catch (Exception ex)
            {
                return $"解压出错:{ex.Message.ToString()}";
            }
            return string.Empty;
        }

        /// <summary>
        /// 目标文件夹打包Zip文件包
        /// </summary>
        /// <param name="startPath">需压缩文件路径</param>
        /// <param name="zipPath">压缩后文件路径</param>
        /// <param name="overwrite">是否覆盖文件</param>
        public static void CreateFromDirectory(string startPath, string zipPath, bool overwrite = true)
        {
            try
            {
                // 文件存在并需覆盖
                if (File.Exists(zipPath.ToCompatibilityPath()) && overwrite)
                {
                    File.Delete(zipPath.ToCompatibilityPath());
                }
                Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
                ZipFile.CreateFromDirectory(startPath.ToCompatibilityPath(), zipPath.ToCompatibilityPath());
            }
            catch (Exception ex)
            {
                throw new Exception("生成压缩包出错");
            }
        }

        /// <summary>
        /// 扫描文件夹
        /// </summary>
        /// <param name="folderPath"></param>
        /// <param name="autoScanSubfolder"></param>
        /// <returns></returns>
        public static List<string> ScanFolder(string folderPath, bool autoScanSubfolder)
        {
            folderPath = folderPath.ToCompatibilityPath();//兼容linux
            return autoScanSubfolder
                ? new DirectoryInfo(folderPath).GetFiles(".", SearchOption.AllDirectories).Select(w => w.Name).ToList()
                : new DirectoryInfo(folderPath).GetFiles().Select(w => w.Name).ToList();
        }
        /// <summary>
        /// 指定文件夹下所有文件(不包含子文件夹)移动到目标文件夹
        /// </summary>
        /// <param name="srcPath"></param>
        /// <param name="destPath"></param>
        public static void CopyDirectory(string srcPath, string destPath)
        {
            try
            {
                DirectoryInfo dir = new DirectoryInfo(srcPath);
                FileSystemInfo[] fileinfo = dir.GetFileSystemInfos();  //获取目录下（不包含子目录）的文件和子目录
                foreach (FileSystemInfo i in fileinfo)
                {
                    if (i is DirectoryInfo)     //判断是否文件夹
                    {
                        //if (!Directory.Exists(destPath + "\\" + i.Name))
                        //{
                        //    Directory.CreateDirectory(destPath + "\\" + i.Name);   //目标目录下不存在此文件夹即创建子文件夹
                        //}
                        //CopyDirectory(i.FullName, destPath + "\\" + i.Name);    //递归调用复制子文件夹
                    }
                    else
                    {
                        File.Copy(i.FullName, destPath + "\\" + i.Name, true);      //不是文件夹即复制文件，true表示可以覆盖同名文件
                    }
                }
            }
            catch
            {
                throw new Exception("复制文件失败：");
            }
        }

        /// <summary>
        /// 文件夹复制,包含子文件夹
        /// </summary>
        /// <param name="sourceDirName">源文件</param>
        /// <param name="destDirName">目标文件</param>
        /// <param name="copySubDirs">是否复测子文件夹</param>
        public static void DirectoryCopy(
            string sourceDirName, string destDirName, bool copySubDirs)
        {
            DirectoryInfo dir = new DirectoryInfo(sourceDirName);
            DirectoryInfo[] dirs = dir.GetDirectories();

            // If the source directory does not exist, throw an exception.
            if (!dir.Exists)
            {
                throw new DirectoryNotFoundException(
                    "Source directory does not exist or could not be found: "
                    + sourceDirName);
            }

            // If the destination directory does not exist, create it.
            if (!Directory.Exists(destDirName))
            {
                Directory.CreateDirectory(destDirName);
            }

            // Get the file contents of the directory to copy.
            FileInfo[] files = dir.GetFiles();

            foreach (FileInfo file in files)
            {
                // Create the path to the new copy of the file.
                string temppath = Path.Combine(destDirName, file.Name);

                // Copy the file.
                file.CopyTo(temppath, false);
            }

            // If copySubDirs is true, copy the subdirectories.
            if (copySubDirs)
            {
                foreach (DirectoryInfo subdir in dirs)
                {
                    // Create the subdirectory.
                    string temppath = Path.Combine(destDirName, subdir.Name);

                    // Copy the subdirectories.
                    DirectoryCopy(subdir.FullName, temppath, copySubDirs);
                }
            }
        }

        /// <summary>
        ///    删除文件 ,如果文件为空不报错
        /// </summary>
        public static void DeleteFile(string filePath)
        {
            filePath = filePath.ToCompatibilityPath();//兼容linux
            if (!string.IsNullOrWhiteSpace(filePath) && File.Exists(filePath))
            {
                File.Delete(filePath);
            }
        }

        /// <summary>
        /// 获取所有文件，包含子文件夹中文件
        /// </summary>
        /// <param name="path">源文件的路径</param>
        /// <param name="files">文件路径</param>
        /// <param name="filter">搜索关键字</param>
        /// <param name="fileExtension">文件类型</param>
        public static void GetAllFiles(string path, IList<string> files, string filter = "", string fileExtension = "")
        {
            try
            {
                Directory.GetFiles(path).Where(f => new FileInfo(f).Name.Contains(filter) || string.IsNullOrWhiteSpace(filter)).Where(
                        f => new FileInfo(f).Extension.Contains(fileExtension) || string.IsNullOrWhiteSpace(fileExtension)
                        ).ToList()
                    .ForEach(f => files.Add(f));

                if (!string.IsNullOrWhiteSpace(filter) && files.Count > 0)  // 找到指定文件就不继续找
                {
                    return;
                }

                Directory.GetDirectories(path).ToList()
                    .ForEach(f => GetAllFiles(f, files, filter, fileExtension));
            }
            catch (UnauthorizedAccessException ex)
            {
            }
        }

        /// <summary>
        ///     DeleteDir 删除目录,如果目录不为空会报错的
        /// </summary>
        /// <param name="dir"></param>
        public static void DeleteDir(string dir)
        {
            dir = dir.ToCompatibilityPath();//兼容linux

            if (dir.Length == 0 || !Directory.Exists(dir)) return;
            foreach (var d in Directory.GetFileSystemEntries(dir))
            {
                if (File.Exists(d))
                {
                    var info = new FileInfo(d) { Attributes = FileAttributes.Normal };
                    info.Delete();
                    //File.Delete(d); //直接删除其中的文件         
                }
                else
                    DeleteDir(d); //递归删除子文件夹 
            }
            Directory.Delete(dir, true); //删除已空文件夹                 
        }
        /// <summary>
        /// 删除指定文件夹内所有文件-不删除文件夹自身
        /// </summary>
        /// <param name="srcPath"></param>
        public static void DeleteDirc(string srcPath)
        {
            try
            {
                DirectoryInfo dir = new DirectoryInfo(srcPath);
                FileSystemInfo[] fileinfo = dir.GetFileSystemInfos();  //返回目录中所有文件和子目录
                foreach (FileSystemInfo i in fileinfo)
                {
                    if (i is DirectoryInfo)            //判断是否文件夹
                    {
                        DirectoryInfo subdir = new DirectoryInfo(i.FullName);
                        subdir.Delete(true);          //删除子目录和文件
                    }
                    else
                    {
                        File.Delete(i.FullName);      //删除指定文件
                    }
                }
            }
            catch
            {
                throw;
            }
        }
        #region 根据文件头判断上传的文件是否为图片(png,jpg,jpeg,gif,bmp)

        /// <param name="filePath">filePath是文件的完整路径 </param>
        /// <returns>返回true或false</returns>
        public static bool IsPicture(Stream fs)
        {
            try
            {
                // 转换成byte,读取图片MIME类型
                Stream stream;
                //int contentLength = file0.ContentLength; //文件长度
                byte[] fileByte = new byte[2];//contentLength，这里我们只读取文件长度的前两位用于判断就好了，这样速度比较快，剩下的也用不到。
                stream = fs;
                stream.Read(fileByte, 0, 2);//contentLength，还是取前两位
                stream.Close();

                string fileFlag = "";
                if (fileByte != null && fileByte.Length > 0)//图片数据是否为空
                {
                    fileFlag = fileByte[0].ToString() + fileByte[1].ToString();
                }
                string[] fileTypeStr = { "255216", "7173", "6677", "13780" };//对应的图片格式jpg,gif,bmp,png
                if (fileTypeStr.Contains(fileFlag))
                {
                    return true;
                }
                else
                {
                    return false;
                }
                //BinaryReader reader = new BinaryReader(fs);
                //string fileClass;
                //byte buffer;
                //buffer = reader.ReadByte();
                //fileClass = buffer.ToString();
                //buffer = reader.ReadByte();
                //fileClass += buffer.ToString();
                //reader.Close();
                //fs.Close();
                //if (fileClass == "255216" || fileClass == "7173" || fileClass == "13780" || fileClass == "6677")
                //    //255216是jpg;7173是gif;6677是BMP,13780是PNG;
                //    //7790是exe,8297是rar
                //{
                //    return true;
                //}
                //else
                //{
                //    return false;
                //}
            }
            catch
            {
                return false;
            }
        }
        #endregion

        /// <summary>
        /// 判断文件是否被其他线程占用
        /// </summary>
        /// <param name="fileName">文件路径</param>
        /// <returns></returns>
        public static bool IsFileInUse(string fileName)
        {
            // true表示正在使用,false没有使用
            bool inUse = true;
            FileStream fs = null;
            try
            {
                using (fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.None))
                {
                    inUse = false;
                }
            }
            catch
            {
                return inUse;
            }
            finally
            {
                if (fs != null)
                {
                    fs.Close();
                }
            }
            return inUse;
        }
    }
}
